@{
    var json = Model.Content switch
    {
        JsonNode jsonNode => jsonNode,
        object someObject => JObject.FromObject(someObject),
        null when Model.SerializedJson?.ToString() is string jsonString && !string.IsNullOrWhiteSpace(jsonString) =>
            JsonNode.Parse(jsonString),
        _ => null,
    } ?? new JsonObject();
    var options = Model.Options as JsonEditorOptions ?? new JsonEditorOptions();
    var name = Model.InputName?.ToString() as string;

    const string blockName = "jsonEditor";
    const string editorName = blockName + "__editor";
    const string inputName = blockName + "__input";

    if (string.IsNullOrWhiteSpace(name)) { name = blockName; }

    var uniqueGuid = Guid.NewGuid();
    var editorId = $"{editorName}_{uniqueGuid:N}";
    var inputId = $"{inputName}_{uniqueGuid:N}";
}
<div id="@editorId" class="@editorName"></div>
<input id="@inputId" class="@inputName" name="@name" type="hidden" />

<style asp-name="@ResourceNames.Library"></style>
<style asp-name="@ResourceNames.Style"></style>
<script asp-name="@ResourceNames.Library" at="Foot"></script>

<script at="Foot" depends-on="jQuery,@ResourceNames.Library">
    jQuery(function ($) {
        const $editor = $('#@editorId');
        const $input = $('#@inputId');

        const options = @Json.Serialize(options);

        options.onChange = function () {
            try {
                $input.val(JSON.stringify(editor.get(), null, 2));

                $input.attr('data-error', 'false');
                $input.attr('data-error-text', null);
            }
            catch (error)
            {
                $input.attr('data-error', 'true');
                $input.attr('data-error-text', error);
                $input.trigger('error');
            }

            $input.trigger('change');
        };

        const editor = new JSONEditor($editor[0], options);
        const json = @Html.Raw(json.ToString());

        editor.set(json);
        options.onChange();
    });
</script>
